//
//  ZHRouter.m
//  ZHRouter_Example
//
//  Created by 赵宏亚 on 2019/8/24.
//  Copyright © 2019 Arthur. All rights reserved.
//

#import "ZHRouter.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>


// 存放router字典
static NSMutableDictionary<NSString *, ZHRouter *> *routerByScheme = nil;
static NSString *ZHRURLGlobalScheme = nil;

@interface ZHRouter ()

// 将所有注册信息存储到这个字典里
@property (nonatomic, strong) NSMutableDictionary<NSString *, ZHRPathComponent *> *pathComponentByKey;
@property (nonatomic, copy) NSString *scheme;

@end

@implementation ZHRouter

// pathComponentByKey 懒加载
- (NSMutableDictionary<NSString *, ZHRPathComponent *> *)pathComponentByKey {
    if (!_pathComponentByKey) {
        _pathComponentByKey = @{}.mutableCopy;
    }
    return _pathComponentByKey;
}

+ (instancetype)globalRouter {
    
    if (!ZHRURLGlobalScheme) {
        NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"ZHRouter.bundle/ZHRouter" ofType:@"plist"]; // 获取plist 文件
        
        // 在指定plist文件中找 Scheme
        if ([[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
            NSDictionary *plist = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
            ZHRURLGlobalScheme = [plist objectForKey:ZHRURLSchemeGlobalKey];
        }
        
        // 没找到把 bundleid 作为 Scheme
        if (!ZHRURLGlobalScheme.length) {
            NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
            ZHRURLGlobalScheme = [infoDictionary objectForKey:@"CFBundleIdentifier"];
        }
        
        // 还没有就直接自定义了
        if (!ZHRURLGlobalScheme.length) {
            ZHRURLGlobalScheme = @"com.gonghui.zh";
        }
    }
    return [self routerForScheme:ZHRURLGlobalScheme]; // 返回一个router
}

+ (void)unRegisterRouterForScheme:(NSString *)scheme
{
    if (!scheme.length) {
        return;
    }
    
    [routerByScheme removeObjectForKey:scheme];
}
+ (void)unRegisterAllRouters
{
    [routerByScheme removeAllObjects];
}

// 注册 就是将所有参数存到一个字典中，然后再扔到一个数组中
- (void)addPathComponent:(NSString *)pathComponentKey
                withName:(NSString *)name
{
    [self addPathComponent:pathComponentKey withName:name handler:nil];
}

// 注册
//handler is a custom module or service init function
- (void)addPathComponent:(NSString *)pathComponentKey
                withName:(NSString *)name
                 handler:(ZHRPathComponentCustomHandler)handler
{
    ZHRPathComponent *pathComponent = [[ZHRPathComponent alloc] init];
    pathComponent.key = pathComponentKey;
    pathComponent.name = name;
    pathComponent.handler = handler;
    [self.pathComponentByKey setObject:pathComponent forKey:pathComponentKey];
}

// 移除注册
- (void)removePathComponent:(NSString *)pathComponentKey
{
    [self.pathComponentByKey removeObjectForKey:pathComponentKey];
}


+ (BOOL)canOpenURL:(NSURL *)URL {
    
    if (!URL) {
        return NO;
    }
    NSString *scheme = URL.scheme;
    if (!scheme.length) {
        return NO;
    }
    
    NSString *host = URL.host;
    
    ZHRUsage usage = [self usage:host];
    if (usage == ZHRUsageUnknown) {
        return NO;
    }
    
    ZHRouter *router = [self routerForScheme:scheme];
    
    // 获取URL后边路径信息
    NSArray<NSString *> *pathComponents = URL.pathComponents;
    
    __block BOOL flag = YES;
    
    [pathComponents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        // 用 . 把所有的信息分割开来
        NSArray<NSString *> * subPaths = [obj componentsSeparatedByString:ZHRURLSubPathSplitPattern];
        
        if (!subPaths.count) {
            flag = NO;
            *stop = NO;
            return;
        }
        
        NSString *pathComponentKey = subPaths.firstObject;
        // 如果字典中没有对应的key，则直接返回
        if (!router.pathComponentByKey[pathComponentKey]) {
            return;
        }

        // 如果存在字典中有对应的值则直接取出class
        ZHRPathComponent *pathComponent = router.pathComponentByKey[pathComponentKey];
        NSString *name = pathComponent.name;
        
        if (!name) {
            flag = NO;
            *stop = NO;
            return;
        }
        
        // 在我看来，只要符合我规定的类型就可以了
        
    }];
    
    return flag;
}


+ (BOOL)openURL:(NSURL *)URL {
    return [self openURL:URL withParams:@{} andThen:nil];
}

+ (BOOL)openURL:(NSURL *)URL
     withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params {
    return [self openURL:URL withParams:params andThen:nil];
}

+ (BOOL)openURL:(NSURL *)URL
     withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params
        andThen:(void(^)(NSString *pathComponentKey, id obj, id returnValue))then {
    
    if (![self canOpenURL:URL]) {
        return NO;
    }
    
    NSString *scheme = URL.scheme;
    ZHRouter *router = [self routerForScheme:scheme];
    
    NSString *host = URL.host;
    ZHRUsage usage = [self usage:host];
    
    ZHRViewControlerEnterMode defaultMode = ZHRViewControlerEnterModePush;
    
    if (URL.fragment.length) { // 查找 # 后边的值 push 或者 present
        defaultMode = [self viewControllerEnterMode:URL.fragment];
    }
    
    // 获取URL后边路径信息
    NSArray<NSString *> *pathComponents = URL.pathComponents;
    
    [pathComponents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        
        if (![obj isEqualToString:@"/"]) { // 只要不是 / 就处理
            
            // 逗号分开获取pathComponentByKey
            NSArray<NSString *> * subPaths = [obj componentsSeparatedByString:ZHRURLSubPathSplitPattern];
            NSString *pathComponentKey = subPaths.firstObject;
            
            NSString *className = @"";
            ZHRPathComponentCustomHandler handler;
            // 根据pathComponentByKey 获取 ZHRPathComponent
            ZHRPathComponent *pathComponent = [router.pathComponentByKey objectForKey:pathComponentKey];
            
            if (pathComponent) { // 如果存在就直接赋值
                className = pathComponent.name;
                handler = pathComponent.handler;
            }
            
            // 获取所有&分开的参数
            NSDictionary<NSString *, NSString *> *queryDic = [self queryDicFromURL:URL];
            
            NSDictionary *handlerDic = [self solveURLParams:queryDic withFuncParams:params];
            
            if (handler) { // 调取block 值就成了
                handler(handlerDic);
                return;
            }
            
            [self jumpWithUsage:usage className:className enterMode:defaultMode];
        }
        
    }];
    
    return YES;
}

// 这种直接跳转的就不传参数
+ (void)jumpWithUsage:(ZHRUsage)usage className:(NSString *)name enterMode:(ZHRViewControlerEnterMode)mode {
    
    Class class = NSClassFromString(name);
    
    if (class) {
        id obj = [[class alloc] init];
        if ([obj isKindOfClass:[UIViewController class]]) {
            [self solveJumpWithViewController:(UIViewController *)obj andJumpMode:mode shouldAnimate:YES];
        }
    }
}


// 根据URL的host字符串，来确定URL为什么类型
+ (ZHRUsage)usage:(NSString *)usagePattern
{
    usagePattern = usagePattern.lowercaseString;
    
    if ([usagePattern isEqualToString:ZHRURLNormalJump]) {
        return ZHRUsageNormalJump;
    }
    return ZHRUsageUnknown;
}

// 根据scheme获取响应的router
+ (instancetype)routerForScheme:(NSString *)scheme
{
    if (!scheme.length) {
        return nil;
    }
    
    ZHRouter *router = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        routerByScheme = @{}.mutableCopy;
    });
    
    if (!routerByScheme[scheme]) {
        router = [[self alloc] init];
        router.scheme = scheme;
        [routerByScheme setObject:router forKey:scheme];
    } else {
        router = [routerByScheme objectForKey:scheme];
    }
    
    return router;
}


// 查找 # 后边的值 push 或者 modal
+ (ZHRViewControlerEnterMode)viewControllerEnterMode:(NSString *)enterModePattern
{
    enterModePattern = enterModePattern.lowercaseString;
    if ([enterModePattern isEqualToString:ZHRURLVCJumpPush]) {
        return ZHRViewControlerEnterModePush;
    } else if ([enterModePattern isEqualToString:ZHRURLVCJumpPresent]) {
        return ZHRViewControlerEnterModePresent;
    }
    return ZHRViewControlerEnterModePush;
}

// query: 就是url中用&分开的参数，此处转为字典（key1=value1&key2=value2）
+ (NSDictionary<NSString *, id> *)queryDicFromURL:(NSURL *)URL
{
    if (!URL) {
        return nil;
    }
    if ([UIDevice currentDevice].systemVersion.floatValue < 8) {
        NSMutableDictionary *dic = @{}.mutableCopy;
        NSString *query = URL.query;
        NSArray<NSString *> *queryStrs = [query componentsSeparatedByString:@"&"];
        [queryStrs enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSArray *keyValue = [obj componentsSeparatedByString:@"="];
            if (keyValue.count >= 2) {
                NSString *key = keyValue[0];
                NSString *value = keyValue[1];
                [dic setObject:value forKey:key];
            }
        }];
        return dic;
    } else {
        NSURLComponents *URLComponents = [NSURLComponents componentsWithURL:URL
                                                    resolvingAgainstBaseURL:NO];
        NSArray *queryItems = URLComponents.queryItems;
        NSMutableDictionary *dic = @{}.mutableCopy;
        for (NSURLQueryItem *item in queryItems) {
            if (item.name && item.value) {
                [dic setObject:item.value forKey:item.name];
            }
        }
        return dic;
    }
}

// json字符串转字典
+ (NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)paramsFromJson:(NSString *)json
{
    if (!json.length) {
        return nil;
    }
    NSError *error;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
    if (error) {
        NSLog(@"BeeHive-ZHRouter [Error] Wrong URL Query Format: \n%@", error.description);
    }
    return dic;
}


// 合并 url 中的参数和原生传过来的参数
+ (NSDictionary<NSString *, id> *)solveURLParams:(NSDictionary<NSString *, id> *)URLParams withFuncParams:(NSDictionary<NSString *, id> *)funcParams {
    if (!URLParams) {
        URLParams = @{};
    }
    NSMutableDictionary<NSString *, id> *params = URLParams.mutableCopy;
    NSArray<NSString *> *funcParamKeys = funcParams.allKeys;
    [funcParamKeys enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [params setObject:funcParams[obj] forKey:obj];
    }];
    return params;
}


// 根据 # 后类型，判断是 push 还是 present
+ (void)solveJumpWithViewController:(UIViewController *)viewController
                        andJumpMode:(ZHRViewControlerEnterMode)enterMode
                      shouldAnimate:(BOOL)animate
{
    UIViewController *currentViewController = [self currentViewController];
    switch (enterMode) {
        case ZHRViewControlerEnterModePush:
            [currentViewController.navigationController pushViewController:viewController animated:animate];
            break;
        case ZHRViewControlerEnterModePresent:
            [currentViewController presentViewController:viewController animated:animate completion:^{
            }];
            break;
            
        default:
            break;
    }
}

// 获取当前vc
+ (UIViewController *)currentViewController
{
    UIViewController *viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    
    while (viewController) {
        if ([viewController isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tbvc = (UITabBarController*)viewController;
            viewController = tbvc.selectedViewController;
        } else if ([viewController isKindOfClass:[UINavigationController class]]) {
            UINavigationController *nvc = (UINavigationController*)viewController;
            viewController = nvc.topViewController;
        } else if (viewController.presentedViewController) {
            viewController = viewController.presentedViewController;
        } else if ([viewController isKindOfClass:[UISplitViewController class]] &&
                   ((UISplitViewController *)viewController).viewControllers.count > 0) {
            UISplitViewController *svc = (UISplitViewController *)viewController;
            viewController = svc.viewControllers.lastObject;
        } else  {
            return viewController;
        }
    }
    return viewController;
}

@end
